/*
	nstilegrid.cpp
	
	Definition file for NSTileGrid class

	This file contains all of the neccessary definitions for the NSTileGrid class.

	Daniel Randle
	July 14 2013
*/

#include <nstilegrid.h>
#include <logfile.h>
#include <nsshader.h>
#include <sstream>
#include <nsmesh.h>
#include <nsobjectset.h>
#include <nsobjectreference.h>
#include <nsobject.h>
#include <nsboundingbox.h>

#ifdef DEBUG
#include <debug.h>
#endif

/*
	class NSTileGrid

*/

NSTileGrid::NSTileGrid(int size): mapSize(size)
{
	// Fill the entire grid with empty reference lists
	layers.resize(Z_GRIDSIZE);
	Layers::iterator iter = layers.begin();
	while (iter != layers.end())
	{
		iter->resize(mapSize);
		xGridSpace::iterator iter2 = iter->begin();
		while (iter2 != iter->end())
		{
			iter2->resize(mapSize);
			++iter2;
		}
		++iter;
	}
	occupiedShader = new NSShader("Occupied Shader", "occupiedtiles.fsh", "occupiedtiles.vsh");
	basicTile = new NSMesh();
	basicTile->loadScene("Meshes/Tiles/DAEgrass.dae");
}

NSTileGrid::~NSTileGrid()
{
	while (occupiedPositions.begin() != occupiedPositions.end() )
	{
		delete occupiedPositions.back();
		occupiedPositions.pop_back();
	}
	delete occupiedShader;
	delete basicTile;
}

bool NSTileGrid::addReference(NSObjectReference* ref, TilePosition indexPos)
{
	layers[indexPos.zIndex][indexPos.xIndex][indexPos.yIndex].push_back(ref);
	occupiedPositions.push_back(new TilePosition(indexPos));
	return true;
}


// For this function I want to calculate the equivalent minimum tile
// index and max tile index for x, y, and z then all tiles between
// these tiles will be occupied by the object
bool NSTileGrid::addReference(NSObjectReference* ref)
{
	if (ref->getBase()->getObjectType() == NSObject::HexTile)
		return addReference(ref, getIndexFromPos(ref->getPosition()) );

	NSBoundingBox::Distances distanceFromCenter = ref->getAABB()->getDistancesFromPoint(ref->getPosition());

	TilePosition centerIndex = getIndexFromPos(ref->getPosition());
	TilePosition minIndex = _getIndexFromDeviation(distanceFromCenter.negative);
	TilePosition maxIndex = _getIndexFromDeviation(distanceFromCenter.positive);
	
	LogFile("centerIndex - " + centerIndex.toString() + "\nminIndex - " + minIndex.toString() + "\nmaxIndex - " + maxIndex.toString());

	for (int posz = 1; posz <= maxIndex.zIndex; ++posz)
	{
		for (int posx = 1; posx <= maxIndex.xIndex; ++posx)
		{
			for (int posy = 1; posy <= maxIndex.yIndex; ++posy)
				addReference(ref, TilePosition(centerIndex.zIndex+posz, centerIndex.xIndex+posx, centerIndex.yIndex+posy));
			for (int negy = 0; negy <= minIndex.yIndex; ++negy)
				addReference(ref, TilePosition(centerIndex.zIndex+posz, centerIndex.xIndex+posx, centerIndex.yIndex-negy));
		}
		for (int negx = 0; negx <= minIndex.xIndex; ++negx)
		{
			for (int posy = 1; posy <= maxIndex.yIndex; ++posy)
				addReference(ref, TilePosition(centerIndex.zIndex+posz, centerIndex.xIndex-negx, centerIndex.yIndex+posy));
			for (int negy = 0; negy <= minIndex.yIndex; ++negy)
				addReference(ref, TilePosition(centerIndex.zIndex+posz, centerIndex.xIndex-negx, centerIndex.yIndex-negy));
		}
	}

	for (int negz = 0; negz <= minIndex.zIndex; ++negz)
	{
		for (int posx = 1; posx <= maxIndex.xIndex; ++posx)
		{
			for (int posy = 1; posy <= maxIndex.yIndex; ++posy)
				addReference(ref, TilePosition(centerIndex.zIndex-negz, centerIndex.xIndex+posx, centerIndex.yIndex+posy));
			for (int negy = 0; negy <= minIndex.yIndex; ++negy)
				addReference(ref, TilePosition(centerIndex.zIndex-negz, centerIndex.xIndex+posx, centerIndex.yIndex-negy));
		}
		for (int negx = 0; negx <= minIndex.xIndex; ++negx)
		{
			for (int posy = 1; posy <= maxIndex.yIndex; ++posy)
				addReference(ref, TilePosition(centerIndex.zIndex-negz, centerIndex.xIndex-negx, centerIndex.yIndex+posy));
			for (int negy = 0; negy <= minIndex.yIndex; ++negy)
				addReference(ref, TilePosition(centerIndex.zIndex-negz, centerIndex.xIndex-negx, centerIndex.yIndex-negy));
		}
	}
	return true;
}

void NSTileGrid::drawOccupiedGridSpaces(const NSMatrix4Df & projCamMat)
{
	std::list<TilePosition*>::iterator iter = occupiedPositions.begin();
	std::vector<NSMatrix4Df> * transforms = new std::vector<NSMatrix4Df>;

	while (iter != occupiedPositions.end())
	{
		NSVec3Df pos;
		pos.x = (*iter)->xIndex * 2 * X_GRID;
		pos.y = (*iter)->yIndex * Y_GRID;
		pos.z = (*iter)->zIndex * Z_GRID + .002f;
		if ( (*iter)->yIndex % 2 != 0 )
			pos.x += X_GRID;

		transforms->push_back(NSMatrix4Df::getTranslationMatrix(pos));
		++iter;
	}

	occupiedShader->enable();
	occupiedShader->setUniform("projCamMat",projCamMat);

	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	basicTile->draw(transforms,true);
	glDisable(GL_BLEND);
	occupiedShader->disable();
	delete transforms;
}

//std::vector<NSTileGrid::TilePosition> NSTileGrid::getOccupiedTilePositions(NSObjectReference* ref)
//{
//	
//}
//
//NSObjectReference * NSTileGrid::getReference(TilePosition pos)
//{
//
//}
//
//NSObjectReference * NSTileGrid::getReference(const NSVec3Df & worldPos)
//{
//
//}

NSTileGrid::TilePosition NSTileGrid::getIndexFromPos(const NSVec3Df & pos)
{
	TilePosition index;

	index.yIndex = _yIndexEquation(pos.y);
	index.zIndex = _zIndexEquation(pos.z);

	if (index.yIndex % 2 != 0)
		index.xIndex = _xIndexEquation(pos.x,true);
	else
		index.xIndex = _xIndexEquation(pos.x);

	return index;
}

//
//bool NSTileGrid::isOccupied(NSTileGrid::TilePosition pos)
//{
//
//}
//
//bool NSTileGrid::isOccupied(const NSVec3Df & worldPos)
//{
//
//}
//
//bool NSTileGrid::isVisible(NSObjectReference * ref)
//{
//
//}
//
//bool NSTileGrid::removeReference(NSObjectReference* ref)
//{
//
//}
//
//bool NSTileGrid::updateReference(NSObjectReference* ref)
//{
//
//}

int NSTileGrid::_xIndexEquation(float x, bool offset)
{
	if (offset)
		return ( (x - X_GRID) / (2 * X_GRID) ) + ROUND_FACTOR;

	return ( x / (2 * X_GRID) ) + ROUND_FACTOR;
}

int NSTileGrid::_yIndexEquation(float y)
{
	return ( y / Y_GRID ) + ROUND_FACTOR;
}

int NSTileGrid::_zIndexEquation(float z)
{
	return ( z / Z_GRID ) + ROUND_FACTOR;
}

NSTileGrid::TilePosition NSTileGrid::_getIndexFromDeviation(NSVec3Df & deviation)
{
	TilePosition index;
	index.xIndex = _xIndexEquation(deviation.x);
	index.yIndex = _yIndexEquation(deviation.y);
	index.zIndex = _zIndexEquation(deviation.z);
	return index;
}

std::string NSTileGrid::TilePosition::toString()
{
	std::stringstream ss;
	ss << "x : " << xIndex << "   y : " << yIndex << "   z : " << zIndex;
	return ss.str();
}